// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/debug/debug-evaluate.h"

#include "src/accessors.h"
#include "src/assembler-inl.h"
#include "src/compiler.h"
#include "src/contexts.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/debug/debug.h"
#include "src/frames-inl.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecodes.h"
#include "src/isolate-inl.h"
#include "src/snapshot/snapshot.h"

namespace v8 {
namespace internal {

    MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
        Handle<String> source,
        bool throw_on_side_effect)
    {
        // Disable breaks in side-effect free mode.
        DisableBreak disable_break_scope(isolate->debug(), throw_on_side_effect);

        Handle<Context> context = isolate->native_context();
        ScriptOriginOptions origin_options(false, true);
        MaybeHandle<SharedFunctionInfo> maybe_function_info = Compiler::GetSharedFunctionInfoForScript(
            isolate, source,
            Compiler::ScriptDetails(isolate->factory()->empty_string()),
            origin_options, nullptr, nullptr, ScriptCompiler::kNoCompileOptions,
            ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE);

        Handle<SharedFunctionInfo> shared_info;
        if (!maybe_function_info.ToHandle(&shared_info))
            return MaybeHandle<Object>();

        Handle<JSFunction> fun = isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info,
            context);
        if (throw_on_side_effect)
            isolate->debug()->StartSideEffectCheckMode();
        MaybeHandle<Object> result = Execution::Call(
            isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0,
            nullptr);
        if (throw_on_side_effect)
            isolate->debug()->StopSideEffectCheckMode();
        return result;
    }

    MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
        StackFrame::Id frame_id,
        int inlined_jsframe_index,
        Handle<String> source,
        bool throw_on_side_effect)
    {
        // Handle the processing of break.
        DisableBreak disable_break_scope(isolate->debug());

        // Get the frame where the debugging is performed.
        StackTraceFrameIterator it(isolate, frame_id);
        if (!it.is_javascript())
            return isolate->factory()->undefined_value();
        JavaScriptFrame* frame = it.javascript_frame();

        // This is not a lot different than DebugEvaluate::Global, except that
        // variables accessible by the function we are evaluating from are
        // materialized and included on top of the native context. Changes to
        // the materialized object are written back afterwards.
        // Note that the native context is taken from the original context chain,
        // which may not be the current native context of the isolate.
        ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
        if (isolate->has_pending_exception())
            return MaybeHandle<Object>();

        Handle<Context> context = context_builder.evaluation_context();
        Handle<JSObject> receiver(context->global_proxy(), isolate);
        MaybeHandle<Object> maybe_result = Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
            throw_on_side_effect);
        if (!maybe_result.is_null())
            context_builder.UpdateValues();
        return maybe_result;
    }

    MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
        Handle<String> source)
    {
        // Handle the processing of break.
        DisableBreak disable_break_scope(isolate->debug());
        Factory* factory = isolate->factory();
        JavaScriptFrameIterator it(isolate);

        // Get context and receiver.
        Handle<Context> native_context(
            Context::cast(it.frame()->context())->native_context(), isolate);

        // Materialize arguments as property on an extension object.
        Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
        Handle<String> arguments_str = factory->arguments_string();
        JSObject::SetOwnPropertyIgnoreAttributes(
            materialized, arguments_str,
            Accessors::FunctionGetArguments(it.frame(), 0), NONE)
            .Check();

        // Materialize receiver.
        Handle<String> this_str = factory->this_string();
        JSObject::SetOwnPropertyIgnoreAttributes(
            materialized, this_str, Handle<Object>(it.frame()->receiver(), isolate),
            NONE)
            .Check();

        // Use extension object in a debug-evaluate scope.
        Handle<ScopeInfo> scope_info = ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
        scope_info->SetIsDebugEvaluateScope();
        Handle<Context> evaluation_context = factory->NewDebugEvaluateContext(native_context, scope_info, materialized,
            Handle<Context>(), Handle<StringSet>());
        Handle<SharedFunctionInfo> outer_info(
            native_context->empty_function()->shared(), isolate);
        Handle<JSObject> receiver(native_context->global_proxy(), isolate);
        const bool throw_on_side_effect = false;
        MaybeHandle<Object> maybe_result = Evaluate(isolate, outer_info, evaluation_context, receiver, source,
            throw_on_side_effect);
        return maybe_result;
    }

    // Compile and evaluate source for the given context.
    MaybeHandle<Object> DebugEvaluate::Evaluate(
        Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
        Handle<Context> context, Handle<Object> receiver, Handle<String> source,
        bool throw_on_side_effect)
    {
        Handle<JSFunction> eval_fun;
        ASSIGN_RETURN_ON_EXCEPTION(
            isolate, eval_fun,
            Compiler::GetFunctionFromEval(source, outer_info, context,
                LanguageMode::kSloppy, NO_PARSE_RESTRICTION,
                kNoSourcePosition, kNoSourcePosition,
                kNoSourcePosition),
            Object);

        Handle<Object> result;
        bool success = false;
        if (throw_on_side_effect)
            isolate->debug()->StartSideEffectCheckMode();
        success = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
                      .ToHandle(&result);
        if (throw_on_side_effect)
            isolate->debug()->StopSideEffectCheckMode();
        if (!success)
            DCHECK(isolate->has_pending_exception());
        return success ? result : MaybeHandle<Object>();
    }

    Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const
    {
        return handle(frame_inspector_.GetFunction()->shared(), isolate_);
    }

    DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
        JavaScriptFrame* frame,
        int inlined_jsframe_index)
        : isolate_(isolate)
        , frame_inspector_(frame, inlined_jsframe_index, isolate)
        , scope_iterator_(isolate, &frame_inspector_,
              ScopeIterator::COLLECT_NON_LOCALS)
    {
        Handle<Context> outer_context(frame_inspector_.GetFunction()->context(),
            isolate);
        evaluation_context_ = outer_context;
        Factory* factory = isolate->factory();

        if (scope_iterator_.Done())
            return;

        // To evaluate as if we were running eval at the point of the debug break,
        // we reconstruct the context chain as follows:
        //  - To make stack-allocated variables visible, we materialize them and
        //    use a debug-evaluate context to wrap both the materialized object and
        //    the original context.
        //  - We use the original context chain from the function context to the
        //    native context.
        //  - Between the function scope and the native context, we only resolve
        //    variable names that the current function already uses. Only for these
        //    names we can be sure that they will be correctly resolved. For the
        //    rest, we only resolve to with, script, and native contexts. We use a
        //    whitelist to implement that.
        // Context::Lookup has special handling for debug-evaluate contexts:
        //  - Look up in the materialized stack variables.
        //  - Look up in the original context.
        //  - Check the whitelist to find out whether to skip contexts during lookup.
        for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
            ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
            if (scope_type == ScopeIterator::ScopeTypeScript)
                break;
            ContextChainElement context_chain_element;
            if (scope_type == ScopeIterator::ScopeTypeLocal || scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
                context_chain_element.materialized_object = scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
            }
            if (scope_iterator_.HasContext()) {
                context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
            }
            if (scope_type == ScopeIterator::ScopeTypeLocal) {
                context_chain_element.whitelist = scope_iterator_.GetNonLocals();
            }
            context_chain_.push_back(context_chain_element);
        }

        Handle<ScopeInfo> scope_info = evaluation_context_->IsNativeContext()
            ? Handle<ScopeInfo>::null()
            : handle(evaluation_context_->scope_info(), isolate);
        for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
             rit++) {
            ContextChainElement element = *rit;
            scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
            scope_info->SetIsDebugEvaluateScope();
            evaluation_context_ = factory->NewDebugEvaluateContext(
                evaluation_context_, scope_info, element.materialized_object,
                element.wrapped_context, element.whitelist);
        }
    }

    void DebugEvaluate::ContextBuilder::UpdateValues()
    {
        scope_iterator_.Restart();
        for (ContextChainElement& element : context_chain_) {
            if (!element.materialized_object.is_null()) {
                Handle<FixedArray> keys = KeyAccumulator::GetKeys(element.materialized_object,
                    KeyCollectionMode::kOwnOnly,
                    ENUMERABLE_STRINGS)
                                              .ToHandleChecked();

                for (int i = 0; i < keys->length(); i++) {
                    DCHECK(keys->get(i)->IsString());
                    Handle<String> key(String::cast(keys->get(i)), isolate_);
                    Handle<Object> value = JSReceiver::GetDataProperty(element.materialized_object, key);
                    scope_iterator_.SetVariableValue(key, value);
                }
            }
            scope_iterator_.Next();
        }
    }

    namespace {

        bool IntrinsicHasNoSideEffect(Runtime::FunctionId id)
        {
// Use macro to include only the non-inlined version of an intrinsic.
#define INTRINSIC_WHITELIST(V)                  \
    /* Conversions */                           \
    V(NumberToString)                           \
    V(ToBigInt)                                 \
    V(ToLength)                                 \
    V(ToNumber)                                 \
    V(ToObject)                                 \
    V(ToString)                                 \
    /* Type checks */                           \
    V(IsArray)                                  \
    V(IsFunction)                               \
    V(IsJSProxy)                                \
    V(IsJSReceiver)                             \
    V(IsRegExp)                                 \
    V(IsSmi)                                    \
    V(IsTypedArray)                             \
    /* Loads */                                 \
    V(LoadLookupSlotForCall)                    \
    V(GetProperty)                              \
    /* Arrays */                                \
    V(ArraySpeciesConstructor)                  \
    V(EstimateNumberOfElements)                 \
    V(GetArrayKeys)                             \
    V(HasComplexElements)                       \
    V(HasFastPackedElements)                    \
    V(NewArray)                                 \
    V(NormalizeElements)                        \
    V(PrepareElementsForSort)                   \
    V(TrySliceSimpleNonFastElements)            \
    V(TypedArrayGetBuffer)                      \
    /* Errors */                                \
    V(NewTypeError)                             \
    V(ReThrow)                                  \
    V(ThrowCalledNonCallable)                   \
    V(ThrowInvalidStringLength)                 \
    V(ThrowIteratorError)                       \
    V(ThrowIteratorResultNotAnObject)           \
    V(ThrowPatternAssignmentNonCoercible)       \
    V(ThrowReferenceError)                      \
    V(ThrowSymbolIteratorInvalid)               \
    /* Strings */                               \
    V(StringIncludes)                           \
    V(StringIndexOf)                            \
    V(StringReplaceOneCharWithString)           \
    V(StringSubstring)                          \
    V(StringToNumber)                           \
    V(StringTrim)                               \
    /* BigInts */                               \
    V(BigIntEqualToBigInt)                      \
    V(BigIntToBoolean)                          \
    V(BigIntToNumber)                           \
    /* Literals */                              \
    V(CreateArrayLiteral)                       \
    V(CreateArrayLiteralWithoutAllocationSite)  \
    V(CreateObjectLiteral)                      \
    V(CreateObjectLiteralWithoutAllocationSite) \
    V(CreateRegExpLiteral)                      \
    /* Called from builtins */                  \
    V(AllocateInYoungGeneration)                \
    V(AllocateInOldGeneration)                  \
    V(AllocateSeqOneByteString)                 \
    V(AllocateSeqTwoByteString)                 \
    V(ArrayIncludes_Slow)                       \
    V(ArrayIndexOf)                             \
    V(ArrayIsArray)                             \
    V(ClassOf)                                  \
    V(GetFunctionName)                          \
    V(GetOwnPropertyDescriptor)                 \
    V(GlobalPrint)                              \
    V(HasProperty)                              \
    V(ObjectCreate)                             \
    V(ObjectEntries)                            \
    V(ObjectEntriesSkipFastPath)                \
    V(ObjectHasOwnProperty)                     \
    V(ObjectValues)                             \
    V(ObjectValuesSkipFastPath)                 \
    V(ObjectGetOwnPropertyNames)                \
    V(ObjectGetOwnPropertyNamesTryFast)         \
    V(RegExpInitializeAndCompile)               \
    V(StackGuard)                               \
    V(StringAdd)                                \
    V(StringCharCodeAt)                         \
    V(StringEqual)                              \
    V(StringIndexOfUnchecked)                   \
    V(StringParseFloat)                         \
    V(StringParseInt)                           \
    V(SymbolDescriptiveString)                  \
    V(ThrowRangeError)                          \
    V(ThrowTypeError)                           \
    V(ToName)                                   \
    V(TransitionElementsKind)                   \
    /* Misc. */                                 \
    V(Call)                                     \
    V(CompleteInobjectSlackTrackingForMap)      \
    V(HasInPrototypeChain)                      \
    V(IncrementUseCounter)                      \
    V(MaxSmi)                                   \
    V(NewObject)                                \
    V(SmiLexicographicCompare)                  \
    V(StringMaxLength)                          \
    V(StringToArray)                            \
    /* Test */                                  \
    V(GetOptimizationStatus)                    \
    V(OptimizeFunctionOnNextCall)               \
    V(OptimizeOsr)                              \
    V(UnblockConcurrentRecompilation)

// Intrinsics with inline versions have to be whitelisted here a second time.
#define INLINE_INTRINSIC_WHITELIST(V) \
    V(Call)                           \
    V(IsJSReceiver)

#define CASE(Name) case Runtime::k##Name:
#define INLINE_CASE(Name) case Runtime::kInline##Name:
            switch (id) {
                INTRINSIC_WHITELIST(CASE)
                INLINE_INTRINSIC_WHITELIST(INLINE_CASE)
                return true;
            default:
                if (FLAG_trace_side_effect_free_debug_evaluate) {
                    PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n",
                        Runtime::FunctionForId(id)->name);
                }
                return false;
            }

#undef CASE
#undef INLINE_CASE
#undef INTRINSIC_WHITELIST
#undef INLINE_INTRINSIC_WHITELIST
        }

        bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode)
        {
            using interpreter::Bytecode;
            using interpreter::Bytecodes;
            if (Bytecodes::IsWithoutExternalSideEffects(bytecode))
                return true;
            if (Bytecodes::IsCallOrConstruct(bytecode))
                return true;
            if (Bytecodes::IsJumpIfToBoolean(bytecode))
                return true;
            if (Bytecodes::IsPrefixScalingBytecode(bytecode))
                return true;
            switch (bytecode) {
            // Whitelist for bytecodes.
            // Loads.
            case Bytecode::kLdaLookupSlot:
            case Bytecode::kLdaGlobal:
            case Bytecode::kLdaNamedProperty:
            case Bytecode::kLdaNamedPropertyNoFeedback:
            case Bytecode::kLdaKeyedProperty:
            case Bytecode::kLdaGlobalInsideTypeof:
            case Bytecode::kLdaLookupSlotInsideTypeof:
            // Arithmetics.
            case Bytecode::kAdd:
            case Bytecode::kAddSmi:
            case Bytecode::kSub:
            case Bytecode::kSubSmi:
            case Bytecode::kMul:
            case Bytecode::kMulSmi:
            case Bytecode::kDiv:
            case Bytecode::kDivSmi:
            case Bytecode::kMod:
            case Bytecode::kModSmi:
            case Bytecode::kExp:
            case Bytecode::kExpSmi:
            case Bytecode::kNegate:
            case Bytecode::kBitwiseAnd:
            case Bytecode::kBitwiseAndSmi:
            case Bytecode::kBitwiseNot:
            case Bytecode::kBitwiseOr:
            case Bytecode::kBitwiseOrSmi:
            case Bytecode::kBitwiseXor:
            case Bytecode::kBitwiseXorSmi:
            case Bytecode::kShiftLeft:
            case Bytecode::kShiftLeftSmi:
            case Bytecode::kShiftRight:
            case Bytecode::kShiftRightSmi:
            case Bytecode::kShiftRightLogical:
            case Bytecode::kShiftRightLogicalSmi:
            case Bytecode::kInc:
            case Bytecode::kDec:
            case Bytecode::kLogicalNot:
            case Bytecode::kToBooleanLogicalNot:
            case Bytecode::kTypeOf:
            // Contexts.
            case Bytecode::kCreateBlockContext:
            case Bytecode::kCreateCatchContext:
            case Bytecode::kCreateFunctionContext:
            case Bytecode::kCreateEvalContext:
            case Bytecode::kCreateWithContext:
            // Literals.
            case Bytecode::kCreateArrayLiteral:
            case Bytecode::kCreateEmptyArrayLiteral:
            case Bytecode::kCreateArrayFromIterable:
            case Bytecode::kCreateObjectLiteral:
            case Bytecode::kCreateEmptyObjectLiteral:
            case Bytecode::kCreateRegExpLiteral:
            // Allocations.
            case Bytecode::kCreateClosure:
            case Bytecode::kCreateUnmappedArguments:
            case Bytecode::kCreateRestParameter:
            // Comparisons.
            case Bytecode::kTestEqual:
            case Bytecode::kTestEqualStrict:
            case Bytecode::kTestLessThan:
            case Bytecode::kTestLessThanOrEqual:
            case Bytecode::kTestGreaterThan:
            case Bytecode::kTestGreaterThanOrEqual:
            case Bytecode::kTestInstanceOf:
            case Bytecode::kTestIn:
            case Bytecode::kTestReferenceEqual:
            case Bytecode::kTestUndetectable:
            case Bytecode::kTestTypeOf:
            case Bytecode::kTestUndefined:
            case Bytecode::kTestNull:
            // Conversions.
            case Bytecode::kToObject:
            case Bytecode::kToName:
            case Bytecode::kToNumber:
            case Bytecode::kToNumeric:
            case Bytecode::kToString:
            // Misc.
            case Bytecode::kForInEnumerate:
            case Bytecode::kForInPrepare:
            case Bytecode::kForInContinue:
            case Bytecode::kForInNext:
            case Bytecode::kForInStep:
            case Bytecode::kThrow:
            case Bytecode::kReThrow:
            case Bytecode::kThrowReferenceErrorIfHole:
            case Bytecode::kThrowSuperNotCalledIfHole:
            case Bytecode::kThrowSuperAlreadyCalledIfNotHole:
            case Bytecode::kIllegal:
            case Bytecode::kCallJSRuntime:
            case Bytecode::kStackCheck:
            case Bytecode::kReturn:
            case Bytecode::kSetPendingMessage:
                return true;
            default:
                return false;
            }
        }

        DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id)
        {
            switch (id) {
            // Whitelist for builtins.
            // Object builtins.
            case Builtins::kObjectConstructor:
            case Builtins::kObjectCreate:
            case Builtins::kObjectEntries:
            case Builtins::kObjectGetOwnPropertyDescriptor:
            case Builtins::kObjectGetOwnPropertyDescriptors:
            case Builtins::kObjectGetOwnPropertyNames:
            case Builtins::kObjectGetOwnPropertySymbols:
            case Builtins::kObjectGetPrototypeOf:
            case Builtins::kObjectIs:
            case Builtins::kObjectIsExtensible:
            case Builtins::kObjectIsFrozen:
            case Builtins::kObjectIsSealed:
            case Builtins::kObjectPrototypeValueOf:
            case Builtins::kObjectValues:
            case Builtins::kObjectPrototypeHasOwnProperty:
            case Builtins::kObjectPrototypeIsPrototypeOf:
            case Builtins::kObjectPrototypePropertyIsEnumerable:
            case Builtins::kObjectPrototypeToString:
            case Builtins::kObjectPrototypeToLocaleString:
            // Array builtins.
            case Builtins::kArrayIsArray:
            case Builtins::kArrayConstructor:
            case Builtins::kArrayIndexOf:
            case Builtins::kArrayPrototypeValues:
            case Builtins::kArrayIncludes:
            case Builtins::kArrayPrototypeEntries:
            case Builtins::kArrayPrototypeFill:
            case Builtins::kArrayPrototypeFind:
            case Builtins::kArrayPrototypeFindIndex:
            case Builtins::kArrayPrototypeFlat:
            case Builtins::kArrayPrototypeFlatMap:
            case Builtins::kArrayPrototypeJoin:
            case Builtins::kArrayPrototypeKeys:
            case Builtins::kArrayPrototypeLastIndexOf:
            case Builtins::kArrayPrototypeSlice:
            case Builtins::kArrayPrototypeSort:
            case Builtins::kArrayPrototypeToLocaleString:
            case Builtins::kArrayPrototypeToString:
            case Builtins::kArrayForEach:
            case Builtins::kArrayEvery:
            case Builtins::kArraySome:
            case Builtins::kArrayConcat:
            case Builtins::kArrayFilter:
            case Builtins::kArrayMap:
            case Builtins::kArrayReduce:
            case Builtins::kArrayReduceRight:
            // Trace builtins.
            case Builtins::kIsTraceCategoryEnabled:
            case Builtins::kTrace:
            // TypedArray builtins.
            case Builtins::kTypedArrayConstructor:
            case Builtins::kTypedArrayPrototypeBuffer:
            case Builtins::kTypedArrayPrototypeByteLength:
            case Builtins::kTypedArrayPrototypeByteOffset:
            case Builtins::kTypedArrayPrototypeLength:
            case Builtins::kTypedArrayPrototypeEntries:
            case Builtins::kTypedArrayPrototypeKeys:
            case Builtins::kTypedArrayPrototypeValues:
            case Builtins::kTypedArrayPrototypeFind:
            case Builtins::kTypedArrayPrototypeFindIndex:
            case Builtins::kTypedArrayPrototypeIncludes:
            case Builtins::kTypedArrayPrototypeJoin:
            case Builtins::kTypedArrayPrototypeIndexOf:
            case Builtins::kTypedArrayPrototypeLastIndexOf:
            case Builtins::kTypedArrayPrototypeSlice:
            case Builtins::kTypedArrayPrototypeSubArray:
            case Builtins::kTypedArrayPrototypeEvery:
            case Builtins::kTypedArrayPrototypeSome:
            case Builtins::kTypedArrayPrototypeToLocaleString:
            case Builtins::kTypedArrayPrototypeFilter:
            case Builtins::kTypedArrayPrototypeMap:
            case Builtins::kTypedArrayPrototypeReduce:
            case Builtins::kTypedArrayPrototypeReduceRight:
            case Builtins::kTypedArrayPrototypeForEach:
            // ArrayBuffer builtins.
            case Builtins::kArrayBufferConstructor:
            case Builtins::kArrayBufferPrototypeGetByteLength:
            case Builtins::kArrayBufferIsView:
            case Builtins::kArrayBufferPrototypeSlice:
            case Builtins::kReturnReceiver:
            // DataView builtins.
            case Builtins::kDataViewConstructor:
            case Builtins::kDataViewPrototypeGetBuffer:
            case Builtins::kDataViewPrototypeGetByteLength:
            case Builtins::kDataViewPrototypeGetByteOffset:
            case Builtins::kDataViewPrototypeGetInt8:
            case Builtins::kDataViewPrototypeGetUint8:
            case Builtins::kDataViewPrototypeGetInt16:
            case Builtins::kDataViewPrototypeGetUint16:
            case Builtins::kDataViewPrototypeGetInt32:
            case Builtins::kDataViewPrototypeGetUint32:
            case Builtins::kDataViewPrototypeGetFloat32:
            case Builtins::kDataViewPrototypeGetFloat64:
            case Builtins::kDataViewPrototypeGetBigInt64:
            case Builtins::kDataViewPrototypeGetBigUint64:
            // Boolean bulitins.
            case Builtins::kBooleanConstructor:
            case Builtins::kBooleanPrototypeToString:
            case Builtins::kBooleanPrototypeValueOf:
            // Date builtins.
            case Builtins::kDateConstructor:
            case Builtins::kDateNow:
            case Builtins::kDateParse:
            case Builtins::kDatePrototypeGetDate:
            case Builtins::kDatePrototypeGetDay:
            case Builtins::kDatePrototypeGetFullYear:
            case Builtins::kDatePrototypeGetHours:
            case Builtins::kDatePrototypeGetMilliseconds:
            case Builtins::kDatePrototypeGetMinutes:
            case Builtins::kDatePrototypeGetMonth:
            case Builtins::kDatePrototypeGetSeconds:
            case Builtins::kDatePrototypeGetTime:
            case Builtins::kDatePrototypeGetTimezoneOffset:
            case Builtins::kDatePrototypeGetUTCDate:
            case Builtins::kDatePrototypeGetUTCDay:
            case Builtins::kDatePrototypeGetUTCFullYear:
            case Builtins::kDatePrototypeGetUTCHours:
            case Builtins::kDatePrototypeGetUTCMilliseconds:
            case Builtins::kDatePrototypeGetUTCMinutes:
            case Builtins::kDatePrototypeGetUTCMonth:
            case Builtins::kDatePrototypeGetUTCSeconds:
            case Builtins::kDatePrototypeGetYear:
            case Builtins::kDatePrototypeToDateString:
            case Builtins::kDatePrototypeToISOString:
            case Builtins::kDatePrototypeToUTCString:
            case Builtins::kDatePrototypeToString:
#ifdef V8_INTL_SUPPORT
            case Builtins::kDatePrototypeToLocaleString:
            case Builtins::kDatePrototypeToLocaleDateString:
            case Builtins::kDatePrototypeToLocaleTimeString:
#endif
            case Builtins::kDatePrototypeToTimeString:
            case Builtins::kDatePrototypeToJson:
            case Builtins::kDatePrototypeToPrimitive:
            case Builtins::kDatePrototypeValueOf:
            // Map builtins.
            case Builtins::kMapConstructor:
            case Builtins::kMapPrototypeForEach:
            case Builtins::kMapPrototypeGet:
            case Builtins::kMapPrototypeHas:
            case Builtins::kMapPrototypeEntries:
            case Builtins::kMapPrototypeGetSize:
            case Builtins::kMapPrototypeKeys:
            case Builtins::kMapPrototypeValues:
            // WeakMap builtins.
            case Builtins::kWeakMapConstructor:
            case Builtins::kWeakMapGet:
            case Builtins::kWeakMapPrototypeHas:
            // Math builtins.
            case Builtins::kMathAbs:
            case Builtins::kMathAcos:
            case Builtins::kMathAcosh:
            case Builtins::kMathAsin:
            case Builtins::kMathAsinh:
            case Builtins::kMathAtan:
            case Builtins::kMathAtanh:
            case Builtins::kMathAtan2:
            case Builtins::kMathCeil:
            case Builtins::kMathCbrt:
            case Builtins::kMathExpm1:
            case Builtins::kMathClz32:
            case Builtins::kMathCos:
            case Builtins::kMathCosh:
            case Builtins::kMathExp:
            case Builtins::kMathFloor:
            case Builtins::kMathFround:
            case Builtins::kMathHypot:
            case Builtins::kMathImul:
            case Builtins::kMathLog:
            case Builtins::kMathLog1p:
            case Builtins::kMathLog2:
            case Builtins::kMathLog10:
            case Builtins::kMathMax:
            case Builtins::kMathMin:
            case Builtins::kMathPow:
            case Builtins::kMathRandom:
            case Builtins::kMathRound:
            case Builtins::kMathSign:
            case Builtins::kMathSin:
            case Builtins::kMathSinh:
            case Builtins::kMathSqrt:
            case Builtins::kMathTan:
            case Builtins::kMathTanh:
            case Builtins::kMathTrunc:
            // Number builtins.
            case Builtins::kNumberConstructor:
            case Builtins::kNumberIsFinite:
            case Builtins::kNumberIsInteger:
            case Builtins::kNumberIsNaN:
            case Builtins::kNumberIsSafeInteger:
            case Builtins::kNumberParseFloat:
            case Builtins::kNumberParseInt:
            case Builtins::kNumberPrototypeToExponential:
            case Builtins::kNumberPrototypeToFixed:
            case Builtins::kNumberPrototypeToPrecision:
            case Builtins::kNumberPrototypeToString:
            case Builtins::kNumberPrototypeToLocaleString:
            case Builtins::kNumberPrototypeValueOf:
            // BigInt builtins.
            case Builtins::kBigIntConstructor:
            case Builtins::kBigIntAsIntN:
            case Builtins::kBigIntAsUintN:
            case Builtins::kBigIntPrototypeToString:
            case Builtins::kBigIntPrototypeValueOf:
            // Set builtins.
            case Builtins::kSetConstructor:
            case Builtins::kSetPrototypeEntries:
            case Builtins::kSetPrototypeForEach:
            case Builtins::kSetPrototypeGetSize:
            case Builtins::kSetPrototypeHas:
            case Builtins::kSetPrototypeValues:
            // WeakSet builtins.
            case Builtins::kWeakSetConstructor:
            case Builtins::kWeakSetPrototypeHas:
            // String builtins. Strings are immutable.
            case Builtins::kStringFromCharCode:
            case Builtins::kStringFromCodePoint:
            case Builtins::kStringConstructor:
            case Builtins::kStringPrototypeAnchor:
            case Builtins::kStringPrototypeBig:
            case Builtins::kStringPrototypeBlink:
            case Builtins::kStringPrototypeBold:
            case Builtins::kStringPrototypeCharAt:
            case Builtins::kStringPrototypeCharCodeAt:
            case Builtins::kStringPrototypeCodePointAt:
            case Builtins::kStringPrototypeConcat:
            case Builtins::kStringPrototypeEndsWith:
            case Builtins::kStringPrototypeFixed:
            case Builtins::kStringPrototypeFontcolor:
            case Builtins::kStringPrototypeFontsize:
            case Builtins::kStringPrototypeIncludes:
            case Builtins::kStringPrototypeIndexOf:
            case Builtins::kStringPrototypeItalics:
            case Builtins::kStringPrototypeLastIndexOf:
            case Builtins::kStringPrototypeLink:
            case Builtins::kStringPrototypeMatchAll:
            case Builtins::kStringPrototypePadEnd:
            case Builtins::kStringPrototypePadStart:
            case Builtins::kStringPrototypeRepeat:
            case Builtins::kStringPrototypeSlice:
            case Builtins::kStringPrototypeSmall:
            case Builtins::kStringPrototypeStartsWith:
            case Builtins::kStringPrototypeStrike:
            case Builtins::kStringPrototypeSub:
            case Builtins::kStringPrototypeSubstr:
            case Builtins::kStringPrototypeSubstring:
            case Builtins::kStringPrototypeSup:
            case Builtins::kStringPrototypeToString:
#ifndef V8_INTL_SUPPORT
            case Builtins::kStringPrototypeToLowerCase:
            case Builtins::kStringPrototypeToUpperCase:
#endif
            case Builtins::kStringPrototypeTrim:
            case Builtins::kStringPrototypeTrimEnd:
            case Builtins::kStringPrototypeTrimStart:
            case Builtins::kStringPrototypeValueOf:
            case Builtins::kStringToNumber:
            case Builtins::kStringSubstring:
            // Symbol builtins.
            case Builtins::kSymbolConstructor:
            case Builtins::kSymbolKeyFor:
            case Builtins::kSymbolPrototypeToString:
            case Builtins::kSymbolPrototypeValueOf:
            case Builtins::kSymbolPrototypeToPrimitive:
            // JSON builtins.
            case Builtins::kJsonParse:
            case Builtins::kJsonStringify:
            // Global function builtins.
            case Builtins::kGlobalDecodeURI:
            case Builtins::kGlobalDecodeURIComponent:
            case Builtins::kGlobalEncodeURI:
            case Builtins::kGlobalEncodeURIComponent:
            case Builtins::kGlobalEscape:
            case Builtins::kGlobalUnescape:
            case Builtins::kGlobalIsFinite:
            case Builtins::kGlobalIsNaN:
            // Function builtins.
            case Builtins::kFunctionPrototypeToString:
            case Builtins::kFunctionPrototypeBind:
            case Builtins::kFastFunctionPrototypeBind:
            case Builtins::kFunctionPrototypeCall:
            case Builtins::kFunctionPrototypeApply:
            // Error builtins.
            case Builtins::kErrorConstructor:
            case Builtins::kMakeError:
            case Builtins::kMakeTypeError:
            case Builtins::kMakeSyntaxError:
            case Builtins::kMakeRangeError:
            case Builtins::kMakeURIError:
            // RegExp builtins.
            case Builtins::kRegExpConstructor:
            // Internal.
            case Builtins::kStrictPoisonPillThrower:
            case Builtins::kAllocateInYoungGeneration:
            case Builtins::kAllocateInOldGeneration:
                return DebugInfo::kHasNoSideEffect;

            // Set builtins.
            case Builtins::kSetIteratorPrototypeNext:
            case Builtins::kSetPrototypeAdd:
            case Builtins::kSetPrototypeClear:
            case Builtins::kSetPrototypeDelete:
            // Array builtins.
            case Builtins::kArrayIteratorPrototypeNext:
            case Builtins::kArrayPrototypePop:
            case Builtins::kArrayPrototypePush:
            case Builtins::kArrayPrototypeReverse:
            case Builtins::kArrayPrototypeShift:
            case Builtins::kArrayPrototypeUnshift:
            case Builtins::kArrayPrototypeSplice:
            case Builtins::kArrayUnshift:
            // Map builtins.
            case Builtins::kMapIteratorPrototypeNext:
            case Builtins::kMapPrototypeClear:
            case Builtins::kMapPrototypeDelete:
            case Builtins::kMapPrototypeSet:
            // RegExp builtins.
            case Builtins::kRegExpPrototypeTest:
            case Builtins::kRegExpPrototypeExec:
            case Builtins::kRegExpPrototypeSplit:
            case Builtins::kRegExpPrototypeFlagsGetter:
            case Builtins::kRegExpPrototypeGlobalGetter:
            case Builtins::kRegExpPrototypeIgnoreCaseGetter:
            case Builtins::kRegExpPrototypeMatchAll:
            case Builtins::kRegExpPrototypeMultilineGetter:
            case Builtins::kRegExpPrototypeDotAllGetter:
            case Builtins::kRegExpPrototypeUnicodeGetter:
            case Builtins::kRegExpPrototypeStickyGetter:
                return DebugInfo::kRequiresRuntimeChecks;
            default:
                if (FLAG_trace_side_effect_free_debug_evaluate) {
                    PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
                        Builtins::name(id));
                }
                return DebugInfo::kHasSideEffects;
            }
        }

        bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode)
        {
            using interpreter::Bytecode;
            switch (bytecode) {
            case Bytecode::kStaNamedProperty:
            case Bytecode::kStaNamedPropertyNoFeedback:
            case Bytecode::kStaNamedOwnProperty:
            case Bytecode::kStaKeyedProperty:
            case Bytecode::kStaInArrayLiteral:
            case Bytecode::kStaDataPropertyInLiteral:
            case Bytecode::kStaCurrentContextSlot:
                return true;
            default:
                return false;
            }
        }

    } // anonymous namespace

    // static
    DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
        Isolate* isolate, Handle<SharedFunctionInfo> info)
    {
        if (FLAG_trace_side_effect_free_debug_evaluate) {
            PrintF("[debug-evaluate] Checking function %s for side effect.\n",
                info->DebugName()->ToCString().get());
        }

        DCHECK(info->is_compiled());
        if (info->HasBytecodeArray()) {
            // Check bytecodes against whitelist.
            Handle<BytecodeArray> bytecode_array(info->GetBytecodeArray(), isolate);
            if (FLAG_trace_side_effect_free_debug_evaluate) {
                bytecode_array->Print();
            }
            bool requires_runtime_checks = false;
            for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
                 it.Advance()) {
                interpreter::Bytecode bytecode = it.current_bytecode();

                if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
                    Runtime::FunctionId id = (bytecode == interpreter::Bytecode::kInvokeIntrinsic)
                        ? it.GetIntrinsicIdOperand(0)
                        : it.GetRuntimeIdOperand(0);
                    if (IntrinsicHasNoSideEffect(id))
                        continue;
                    return DebugInfo::kHasSideEffects;
                }

                if (BytecodeHasNoSideEffect(bytecode))
                    continue;
                if (BytecodeRequiresRuntimeCheck(bytecode)) {
                    requires_runtime_checks = true;
                    continue;
                }

                if (FLAG_trace_side_effect_free_debug_evaluate) {
                    PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
                        interpreter::Bytecodes::ToString(bytecode));
                }

                // Did not match whitelist.
                return DebugInfo::kHasSideEffects;
            }
            return requires_runtime_checks ? DebugInfo::kRequiresRuntimeChecks
                                           : DebugInfo::kHasNoSideEffect;
        } else if (info->IsApiFunction()) {
            if (info->GetCode()->is_builtin()) {
                return info->GetCode()->builtin_index() == Builtins::kHandleApiCall
                    ? DebugInfo::kHasNoSideEffect
                    : DebugInfo::kHasSideEffects;
            }
        } else {
            // Check built-ins against whitelist.
            int builtin_index = info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId;
            if (!Builtins::IsBuiltinId(builtin_index))
                return DebugInfo::kHasSideEffects;
            DebugInfo::SideEffectState state = BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index));
            return state;
        }

        return DebugInfo::kHasSideEffects;
    }

#ifdef DEBUG
    static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtins::Name caller,
        Builtins::Name callee)
    {
        switch (callee) {
            // Transitively called Builtins:
        case Builtins::kAbort:
        case Builtins::kAbortJS:
        case Builtins::kAdaptorWithBuiltinExitFrame:
        case Builtins::kArrayConstructorImpl:
        case Builtins::kArrayEveryLoopContinuation:
        case Builtins::kArrayFilterLoopContinuation:
        case Builtins::kArrayFindIndexLoopContinuation:
        case Builtins::kArrayFindLoopContinuation:
        case Builtins::kArrayForEachLoopContinuation:
        case Builtins::kArrayIncludesHoleyDoubles:
        case Builtins::kArrayIncludesPackedDoubles:
        case Builtins::kArrayIncludesSmiOrObject:
        case Builtins::kArrayIndexOfHoleyDoubles:
        case Builtins::kArrayIndexOfPackedDoubles:
        case Builtins::kArrayIndexOfSmiOrObject:
        case Builtins::kArrayMapLoopContinuation:
        case Builtins::kArrayReduceLoopContinuation:
        case Builtins::kArrayReduceRightLoopContinuation:
        case Builtins::kArraySomeLoopContinuation:
        case Builtins::kArrayTimSort:
        case Builtins::kCall_ReceiverIsAny:
        case Builtins::kCall_ReceiverIsNullOrUndefined:
        case Builtins::kCallWithArrayLike:
        case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
        case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
        case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
        case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
        case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit:
        case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
        case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
        case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
        case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
        case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit:
        case Builtins::kCloneFastJSArray:
        case Builtins::kConstruct:
        case Builtins::kConvertToLocaleString:
        case Builtins::kCreateTypedArray:
        case Builtins::kDirectCEntry:
        case Builtins::kDoubleToI:
        case Builtins::kExtractFastJSArray:
        case Builtins::kFastNewObject:
        case Builtins::kFindOrderedHashMapEntry:
        case Builtins::kFlatMapIntoArray:
        case Builtins::kFlattenIntoArray:
        case Builtins::kGetProperty:
        case Builtins::kHasProperty:
        case Builtins::kCreateHTML:
        case Builtins::kNonNumberToNumber:
        case Builtins::kNonPrimitiveToPrimitive_Number:
        case Builtins::kNumberToString:
        case Builtins::kObjectToString:
        case Builtins::kOrderedHashTableHealIndex:
        case Builtins::kOrdinaryToPrimitive_Number:
        case Builtins::kOrdinaryToPrimitive_String:
        case Builtins::kParseInt:
        case Builtins::kProxyHasProperty:
        case Builtins::kRecordWrite:
        case Builtins::kStringAdd_CheckNone:
        case Builtins::kStringEqual:
        case Builtins::kStringIndexOf:
        case Builtins::kStringRepeat:
        case Builtins::kToInteger:
        case Builtins::kToInteger_TruncateMinusZero:
        case Builtins::kToLength:
        case Builtins::kToName:
        case Builtins::kToObject:
        case Builtins::kToString:
        case Builtins::kWeakMapLookupHashIndex:
            return true;
        case Builtins::kJoinStackPop:
        case Builtins::kJoinStackPush:
            switch (caller) {
            case Builtins::kArrayPrototypeJoin:
            case Builtins::kArrayPrototypeToLocaleString:
            case Builtins::kTypedArrayPrototypeJoin:
            case Builtins::kTypedArrayPrototypeToLocaleString:
                return true;
            default:
                return false;
            }
        case Builtins::kFastCreateDataProperty:
            switch (caller) {
            case Builtins::kArrayPrototypeSlice:
            case Builtins::kArrayFilter:
                return true;
            default:
                return false;
            }
        case Builtins::kSetProperty:
            switch (caller) {
            case Builtins::kArrayPrototypeSlice:
            case Builtins::kTypedArrayPrototypeMap:
            case Builtins::kStringPrototypeMatchAll:
                return true;
            default:
                return false;
            }
        default:
            return false;
        }
    }

    // static
    void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate)
    {
        // TODO(yangguo): also check runtime calls.
        bool failed = false;
        bool sanity_check = false;
        for (int i = 0; i < Builtins::builtin_count; i++) {
            Builtins::Name caller = static_cast<Builtins::Name>(i);
            DebugInfo::SideEffectState state = BuiltinGetSideEffectState(caller);
            if (state != DebugInfo::kHasNoSideEffect)
                continue;
            Code code = isolate->builtins()->builtin(caller);
            int mode = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);

            for (RelocIterator it(code, mode); !it.done(); it.next()) {
                RelocInfo* rinfo = it.rinfo();
                DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode()));
                Code callee_code = isolate->heap()->GcSafeFindCodeForInnerPointer(
                    rinfo->target_address());
                if (!callee_code->is_builtin())
                    continue;
                Builtins::Name callee = static_cast<Builtins::Name>(callee_code->builtin_index());
                if (BuiltinGetSideEffectState(callee) == DebugInfo::kHasNoSideEffect) {
                    continue;
                }
                if (TransitivelyCalledBuiltinHasNoSideEffect(caller, callee)) {
                    sanity_check = true;
                    continue;
                }
                PrintF("Whitelisted builtin %s calls non-whitelisted builtin %s\n",
                    Builtins::name(caller), Builtins::name(callee));
                failed = true;
            }
        }
        CHECK(!failed);
#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_MIPS64)
        // Isolate-independent builtin calls and jumps do not emit reloc infos
        // on PPC. We try to avoid using PC relative code due to performance
        // issue with especially older hardwares.
        // MIPS64 doesn't have PC relative code currently.
        // TODO(mips): Add PC relative code to MIPS64.
        USE(sanity_check);
#else
        CHECK(sanity_check);
#endif
    }
#endif // DEBUG

    // static
    void DebugEvaluate::ApplySideEffectChecks(
        Handle<BytecodeArray> bytecode_array)
    {
        for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
             it.Advance()) {
            interpreter::Bytecode bytecode = it.current_bytecode();
            if (BytecodeRequiresRuntimeCheck(bytecode))
                it.ApplyDebugBreak();
        }
    }

} // namespace internal
} // namespace v8
